#include "Hostile.h"

///////////////////////////////////////////////////////////////
// Default constructor.
///////////////////////////////////////////////////////////////
Hostile::Hostile( char * fileName, int id, Pathfinder & pather, Grid<char> & occGrid,
		float walkSpeed, float turnSpeed)
		: Agent( fileName, id, pather, occGrid, walkSpeed, turnSpeed ), isFlipped( false )
{
}

///////////////////////////////////////////////////////////////
// Default destructor.
///////////////////////////////////////////////////////////////
Hostile::~Hostile(void)
{
}


///////////////////////////////////////////////////////////////
// Getters and Setters
///////////////////////////////////////////////////////////////

// get hostile ID
int Hostile::getID() {
	return this->hostileID;
}

// get state.
int Hostile::getState() {
	return this->currentState;
}

// set state.
void Hostile::setState(int state) {
	this->currentState = state;
}



///////////////////////////////////////////////////////////////
// FSM checkState() code.  enum defined in .h file.
///////////////////////////////////////////////////////////////

// check state, initiate actions.
void Hostile::checkHostileState() {
	int s = this->currentState;

	// PATROL, FROZEN, CAPTURED 
	switch (s) {
		case PATROL:
			// animate patrol, either have waypoints Start,End defined, or just animate character.
			// animatePatrol();
			break;
		case FROZEN:
			// do nothing, is frozen (innocents cannot run yet).
			break;
		case CAPTURED:
			// innocents can now run away.
			// do memory clean up on this object.
			break;
		default:
			// default case.
			break;
	}
}

// check for event updates from environment, other objects, user, etc (should be pulled out eventually).
void Hostile::checkHostileEvent(int eventID) {
	switch (eventID) {
		case SEEN_BY_ROBOT:
			// change state to frozen.
			playAnimation(Animation::Die);
			break;
		case PC_ATTACK:
			// do attack maneuvors.
			break;
		default:
			// do nothing.
			break;
	}
}

void Hostile::update()
{
	if (robotNear() == true)
	{
		dbSetObjectAmbience(this->getID(), dbRGB(255,0,0));
		// change state, engage?
		setState(SEEN_BY_ROBOT);
		changedState = oldState == getState() ? false : true;

		// perhaps draw a big blue box as a test?
	}

	int newState = getState();
	if (changedState) {
		switch( newState )
		{
		case Hostile::Walking:
			playAnimation(Animation::Move );
			break;
		case Hostile::Idle:
			playAnimation(Animation::Idle );
			break;
		case Hostile::Attacking:
			playAnimation(Animation::Attack );
			// trigger event for hostile to die.
			break;
		default:
			break;
		}

		changedState = false;
	}
}

bool Hostile::robotNear()
{
	bool killzone = false;				// default value set to false.
	int distance = 30;					// distance our dude can "see"
	
	Position squaresInLOS[5];			// array to store 3D values in line-of-sight
	Triple gridSquaresInLOS[5];			// DEBUG: array to store grid values in line-of-sight

	// get current robot position
	Triple p = getPosition();

	// calculate various offsets for N/W/E/NW/NE
	float direction = dbWrapValue(getRotY());
	float newX = dbNewXValue( p.x, direction, distance );
	float newZ = dbNewZValue( p.z, direction, distance );
	Triple N = Triple( newX, p.y, newZ );

	direction = dbWrapValue(getRotY() - 90 );
	newX = dbNewXValue( p.x, direction, distance );
	newZ = dbNewZValue( p.z, direction, distance );
	Triple W = Triple( newX, p.y, newZ );

	direction = dbWrapValue(getRotY() + 90);
	newX = dbNewXValue( p.x, direction, distance );
	newZ = dbNewZValue( p.z, direction, distance );
	Triple E = Triple( newX, p.y, newZ );

	direction = dbWrapValue(getRotY() - 45);
	newX = dbNewXValue( p.x, direction, distance );
	newZ = dbNewZValue( p.z, direction, distance );
	Triple NW = Triple( newX, p.y, newZ );

	direction = dbWrapValue(getRotY() + 45);
	newX = dbNewXValue( p.x, direction, distance );
	newZ = dbNewZValue( p.z, direction, distance );
	Triple NE = Triple( newX, p.y, newZ );

	// build 3D line-of-sight vector with offset of distance
	squaresInLOS[0] = pathfinder.posToGrid(N);				// check N
	squaresInLOS[1] = pathfinder.posToGrid(W);				// check W
	squaresInLOS[2] = pathfinder.posToGrid(E);				// check E
	squaresInLOS[3] = pathfinder.posToGrid(NW);				// check NW
	squaresInLOS[4] = pathfinder.posToGrid(NE);				// check NE

	// DEBUG: build GRID line-of-sight vector with offset of distance
	gridSquaresInLOS[0] = N;
	gridSquaresInLOS[1] = W;
	gridSquaresInLOS[2] = E;
	gridSquaresInLOS[3] = NW;
	gridSquaresInLOS[4] = NE;

	// DEBUG: color grids we are checking
	for (int i=0; i<5; i++)
	{
		int tmpID = 5500+i;
		dbMakeObjectPlane		( tmpID , 30, 30 );
		dbXRotateObject			( tmpID , -90 );
		dbPositionObject		( tmpID , gridSquaresInLOS[i].x, 0.5, gridSquaresInLOS[i].z);
		dbColorObject			( tmpID, dbRGB(255,0,0) );
		dbSetObjectCollisionOff ( tmpID );
		dbGhostObjectOn( tmpID );
	}

	// check line-of-sight vector.
	for (int i=0; i<5; i++)
	{
		// TODO: check for occlusion
		int foo = occGrid(squaresInLOS[i].x, squaresInLOS[i].z);

		if (foo == 4) // hardcoded id for robot on occupancy grid
		{
			setState(SEEN_BY_ROBOT);
			killzone = true;
		}
	}

	return killzone;
}